using System;
using System.Reflection;

namespace Sedna.Core.Common.Objects.Navigation
{
	/// <summary>
	/// Represents single parsed node in the navigation expression.
	/// </summary>
    [Serializable]
    abstract public class NavigationNode
	{
		protected Type type = null;
		protected NavigationNode parent;

		public object Value
		{
			get { return GetValue(false); }
			set { SetValue(value);}
		}

        /// <summary>
        /// Creates instance of the node.
        /// </summary>
        /// <param name="parent">Parent node.</param>
        /// <param name="nodeExpression">This node's expression.</param>
        /// <param name="nodeType">Node type. Optional, used for automatic object creation.</param>
		public NavigationNode(NavigationNode parent, string nodeExpression, Type nodeType)
		{
			this.type=nodeType;
			this.parent=parent;
		}

		/// <summary>
		/// Returns node's value. Creates object, if node value null & auto create param is set to true.
		/// </summary>
		/// <param name="autoCreate">Determines wether automatic creation is on.</param>
		/// <returns>Node's value.</returns>
		public object GetValue(bool autoCreate)
		{
			object value=GetValueRecursive(autoCreate);
			if (value!=null||!autoCreate)
			{
				return value;
			}
			value=CreateValue();
			SetValue(value);
			return value;
		}

		protected virtual object CreateValue()
		{
			if (type==null)
			{
				throw new ArgumentNullException("Node type is null. Cannot create node object");
			}
			ConstructorInfo constructor=type.GetConstructor(new Type[]{});
			if (constructor==null)
			{
				throw new Exception(String.Format("Type {0} has no default constructor. Cannot create node object.", type));
			}
			object value=constructor.Invoke(new object[]{});
			return value;
		}

		protected abstract object GetValueRecursive(bool autoCreate);

        /// <summary>
        /// Sets node's value.
        /// </summary>
        /// <param name="newValue">New value for this node.</param>
        protected abstract void SetValue(object newValue);

    }
}
